package com.ruoyi.project.applet.util;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Random;

import org.apache.commons.codec.binary.Base64;
import org.springframework.util.StringUtils;

public class VechatPayV3Util {

    /**
     * 生成支付令牌
     *
     * @param method        请求方法 post
     * @param canonicalUrl  请求地址
     * @param body          请求参数
     * @param merchantId    商户号
     * @param certSerialNo  商户证书序列号
     * @param keyPath       商户证书地址
     * @return 支付令牌
     * @throws Exception 异常
     */
    public static String getToken(
            String method,
            String canonicalUrl,
            String body,
            String merchantId,
            String certSerialNo,
            String keyPath) throws Exception {
        String signStr;
        String nonceStr = getRandomString(32);
        long timestamp = System.currentTimeMillis() / 1000;

        if (StringUtils.isEmpty(body)) {
            body = "";
        }

        String message = buildMessage(method, canonicalUrl, timestamp, nonceStr, body);
        String signature = sign(message.getBytes("utf-8"), keyPath);

        signStr = "mchid=\"" + merchantId
                + "\",timestamp=\"" + timestamp
                + "\",nonce_str=\"" + nonceStr
                + "\",serial_no=\"" + certSerialNo
                + "\",signature=\"" + signature + "\"";

        return "WECHATPAY2-SHA256-RSA2048 "+signStr;
    }

    /**
     * 构建签名消息
     */
    public static String buildMessage(
            String method,
            String canonicalUrl,
            long timestamp,
            String nonceStr,
            String body) {
        return method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }

    /**
     * RSA 签名
     */
    public static String sign(byte[] message, String keyPath) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey(keyPath));
        sign.update(message);
        return Base64.encodeBase64String(sign.sign());
    }

    /**
     * 获取商户私钥
     *
     * @param filename 私钥文件路径
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey(String filename) throws IOException {
        String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");

        try {
            String privateKey = content
                    .replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }

    /**
     * 生成随机字符串
     *
     * @param length 字符串长度
     * @return 随机字符串
     */
    public static String getRandomString(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();

        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }

        return sb.toString();
    }
}